#pragma once
// 这个文件包含了这个项目所要用到的所有公共方法
#include <iostream>
#include <assert.h>
#include <thread>
#include <mutex>
#include <cstdlib>

// ThreadCache最大的hash桶的数量是208
const static size_t MAX_BUCKET = 208;
// 内存池最多可以管理256kb大小的空间
const static size_t MAX_BYTES = 1024 * 256;
// 大块页保存的最大的页的内存大小是128kb，所以需要128个桶
const static size_t MAX_PAGENUM = 129;
// 由于内存池页的单位为8kb，8kb=8*1024字节，也就是13位
const static size_t BIT_SHIFT = 13;


#ifdef _WIN64
typedef unsigned long long PAGE_ID;
#elif _WIN32
typedef size_t PAGE_ID;
#else
typedef size_t PAGE_ID;
#endif

// 直接去堆上按页申请空间
inline static void *SystemAlloc(size_t kpage)
{
#ifdef _WIN32
    void *ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
    // 在 linux/类 unix 平台上，简单使用 malloc 作为占位实现。
    // 注意：真实的内核/高性能实现应使用 mmap/brk 并考虑页对齐和保护属性。
    size_t bytes = kpage << BIT_SHIFT;
    void *ptr = std::malloc(bytes);
#endif

    if (ptr == nullptr)
        throw std::bad_alloc();

    return ptr;
}

// 为了增加代码的可读性，传引用是为了后续可以修改指针的指向
static void *&NextObj(void *obj)
{
    return *(void **)obj;
}
// 这个类是ThreadCache中自由链表的实现，包括增删等功能
class FreeList
{
public:
    FreeList() : _freelist(nullptr), _maxsize(1), _size(0)
    {
    }
    void Push(void *obj)
    {
        assert(obj);
        // 为了提高效率，这里使用头插
        // 我们使用前四个或八个字节当作指针，指向下一个节点
        // 这里使用二级指针是为了跨平台的问题，32位机器下是前四个字节，64位机器下是前八个字节
        //  *(void**)obj=_freelist;
        NextObj(obj) = _freelist; // 这样写增加了代码的可读性
        _freelist = obj;
        ++_size;
    }

    void PushRange(void *start, void *end, size_t num)
    {
        NextObj(end) = _freelist;
        _freelist = start;
        _size += num;
    }
    // 返回地址，方便上层获取
    void *Pop()
    {
        assert(_freelist);
        // 头删_freelist->obj->nullptr
        void *obj = _freelist;
        _freelist = NextObj(obj);
        --_size;
        return obj;
    }

    void PopRange(void *&start, void *&end, size_t size)
    {
        // size must be >0 and <= current size
        assert(size > 0 && size <= _size);
        start = _freelist;
        end = start;
        for (size_t i = 0; i < size - 1; i++)
        {
            end = NextObj(end);
        }
        _freelist = NextObj(end);
        NextObj(end) = nullptr;
        _size -= size;
    }

    // 判断自由链表是否为空
    bool Empty()
    {
        return _freelist == nullptr;
    }

    size_t &GetMaxSize()
    {
        return _maxsize;
    }

    size_t GetSize()
    {
        return _size;
    }
    ~FreeList() {}

private:
    void *_freelist; // 由于自由链表可以保存多种不同类型的数据，所以这里使用void*
    size_t _maxsize; // 当前自由链表下面挂的最多的内存块，负责慢反馈调节的参数
    size_t _size;    // 用来记录自由链表当中的节点数
};

class SizeClass
{
public:
    // 整体控制在最多10%左右的内碎片浪费
    // [1,128]					8byte对齐	    freelist[0,16)
    // [128+1,1024]				16byte对齐	    freelist[16,72)
    // [1024+1,8*1024]			128byte对齐	    freelist[72,128)
    // [8*1024+1,64*1024]		1024byte对齐     freelist[128,184)
    // [64*1024+1,256*1024]		8*1024byte对齐   freelist[184,208)
    // 这是传统的获取下标索引的方法，主要用到了除模运算，但是在计算机的世界里，相比于直接操作二进制，除模运算显然效率太低了
    // 为了增加效率，我们使用移位操作
    // size_t _Index(size_t size,size_t alignsize)
    // {
    //     if(size%alignsize==0)
    //     {
    //         return size/alignsize-1;
    //     }
    //     else
    //     {
    //         return size/alignsize;
    //     }
    // }
    static size_t _Index(size_t size, size_t align_shift)
    {
        return ((size + (1 << align_shift) - 1) >> align_shift) - 1;
    }
    static size_t Index(size_t size)
    {
        assert(size <= MAX_BYTES);
        // 每个区间有多少个桶
        size_t arry[4] = {16, 56, 56, 56};
        if (size <= 128)
        {
            // 我们这里第二个参数传递的是对齐数
            //  return _Index(size,8);
            // 改进之后，第二个参数传递二进制位
            return _Index(size, 3);
        }
        else if (size <= 1024)
        {
            // return _Index(size,16)+arry[0];
            return _Index(size, 4) + arry[0];
        }
        else if (size <= 8 * 1024)
        {
            // return _Index(size,128)+arry[0]+arry[1];
            return _Index(size, 7) + arry[0] + arry[1];
        }
        else if (size <= 64 * 1024)
        {
            // return _Index(size,1024)+arry[0]+arry[1]+arry[2];
            return _Index(size, 10) + arry[0] + arry[1] + arry[2];
        }
        else if (size <= 256 * 1024)
        {
            // return _Index(size,8*1024)+arry[0]+arry[1]+arry[2]+arry[3];
            return _Index(size, 13) + arry[0] + arry[1] + arry[2] + arry[3];
        }
        else
        {
            assert("false");
        }
        return -1;
    }

    // 这是常规写法，为了提高效率，我们直接采用移位操作
    //  static size_t _AlignSize(size_t size,size_t alignNum)
    //  {
    //      if(size%alignNum==0){return size;}
    //      else
    //      {
    //          return (size/alignNum+1)*alignNum;
    //      }
    //  }
    static size_t _AlignSize(size_t size, size_t alignNum)
    {
        return (size + alignNum - 1) & ~(alignNum - 1);
    }
    static size_t AlignSize(size_t size)
    {
        assert(size <= MAX_BYTES);
        if (size <= 128)
        {
            return _AlignSize(size, 8);
        }
        else if (size <= 1024)
        {
            return _AlignSize(size, 16);
        }
        else if (size <= 8 * 1024)
        {
            return _AlignSize(size, 128);
        }
        else if (size <= 64 * 1024)
        {
            return _AlignSize(size, 1024);
        }
        else if (size <= 256 * 1024)
        {
            return _AlignSize(size, 8 * 1024);
        }
        else
        {
            assert("false");
        }
    }

    // 从中心缓存获取的内存块数量
    static size_t CacheBlockNum(size_t size)
    {
        assert(size >= 0);
        int num = MAX_BYTES / size;
        if (num < 2)
        {
            num = 2;
        }
        if (num > 512)
        {
            num = 512;
        }
        return num;
    }

    // 根据对齐数计算页的大小
    static size_t CalculatePageSize(size_t size)
    {
        size_t num = CacheBlockNum(size);
        size_t pageSize = num * size;
        pageSize >>= BIT_SHIFT; // 除以8k，得到了页的大小
        if (pageSize == 0)
        {
            pageSize = 1;
        }
        return pageSize;
    }
};

// 用来管理Span
struct Span
{
    PAGE_ID _pageid = 0; // 大块内存起始页的页号
    size_t _pagenum = 0; // 标识当前span在hash桶的第几号桶

    size_t _useCount = 0; // 切好的小块内存，分配给thread cache的引用计数

    // 双向链表结构
    Span *_prev = nullptr;
    Span *_next = nullptr;

    // 自由链表结构
    void *_freelist = nullptr;

    bool _isUse = false; //用来标识当前span是否被使用
};

// Span跨度结构所组成的带头双向循环链表
class SpanList
{
public:
    // 使用哨兵节点初始化，避免对空指针解引用
    SpanList()
    {
        _head = new Span();
        _head->_next = _head;
        _head->_prev = _head;
    }
    Span *Begin()
    {
        assert(_head);
        return _head->_next;
    }
    Span *End()
    {
        return _head;
    }

    void Insert(Span *pos, Span *newSpan)
    {
        assert(pos);
        assert(newSpan);
        // prev pos
        Span *prev = pos->_prev;
        newSpan->_next = pos;
        newSpan->_prev = prev;
        prev->_next = newSpan;
        pos->_prev = newSpan;
    }

    void Erase(Span *pos)
    {
        assert(pos);
        // cannot erase the sentinel head with this method
        assert(pos != _head);
        // prev pos next
        Span *prev = pos->_prev;
        Span *next = pos->_next;

        prev->_next = next;
        next->_prev = prev;
    }

    void PushFront(Span *span)
    {
        Insert(Begin(), span);
    }

    Span *PopFront()
    {
        assert(!Empty());
        Span *front = _head->_next;
        Erase(front);
        return front;
    }
    bool Empty()
    {
        return _head->_next == _head;
    }
    ~SpanList()
    {
        // 删除所有节点并释放哨兵
        // 注意：在程序正常运行中，Span 的内存可能由其他模块管理，
        // 这里的删除仅用于程序退出时的清理（避免内存泄露告警）。
        while (!Empty())
        {
            Span *s = PopFront();
            delete s;
        }
        // 删除哨兵节点
        delete _head;
        _head = nullptr;
    }

public:
    //桶锁
    std::mutex _mutex;
private:
    Span *_head;
};